#include "HndFToRawF.h"
#include <memory>

//! This function converts the unfiltered hnd file data
//! and converts it to raw image file data.
//! \param[in] hnd The unfiltered hnd image file data
//! \param[out] raw The pointer to write the raw file
//! data to. The memory that the pointer points to
//! must be allocated by (sizeof(uint32) * HND_ROWS * HND_COLS).
void HndFToRawF(FILE* hndfile, FILE* rawfile)
{
	// Contents of new raw file
	static uint16 raw[RAW_ROWS * RAW_COLS];
	
	// Contents of HND file
	static uint32 hnd[HND_ROWS * HND_COLS];

	// Lookup table size (in bytes)
	static const uint lutSize = (HND_COLS * (HND_ROWS - 1)) / 4;

	// Lookup table
	static uint8 lut[lutSize];

	fseek(hndfile, 1024, SEEK_SET);
	
	// Read lut
	fread((void*)lut, lutSize, 1, hndfile);

	// Read uncompressed first row of pixels and first pixel of next row
	fread(hnd, (HND_COLS + 1) * sizeof(uint32), 1, hndfile);

	// Index of pixel we are on.
	uint i = HND_COLS + 1;

	uint lutoff, lutid;
	uint32 r11, r12, r21;
	uint8 lutval;
	int diff;
	for (lutoff = lutid = lutval = 0; i < HND_ROWS * HND_COLS; ++i)
	{
		r11 = hnd[i - HND_COLS - 1];
		r12 = hnd[i - HND_COLS];
		r21 = hnd[i - 1];
		lutval = lut[lutid];

		// Since the lookup table is a table of 2 bit values,
		// we must keep an offset variable to track how many
		// entries (of two bits) we have gone into in a uint8.
		switch (lutoff)
		{
		case 0:
			// 0x03 equals 00000011
			lutval = lutval & 0x03;
			++lutoff;
			break;
		case 1:
			// 0x0C equals 00001100
			lutval = (lutval & 0x0C) >> 2;
			++lutoff;
			break;
		case 2:
			// 0x30 equals 00110000
			lutval = (lutval & 0x30) >> 4;
			++lutoff;
			break;
		case 3:
			// 0xC0 equals 11000000
			lutval = (lutval & 0xC0) >> 6;
			lutoff = 0;
			++lutid;
			break;
		}
		char  bchar = 0;
		short bshort = 0;
		long  blong = 0;
		switch (lutval)
		{
		case 0:
			fread(&bchar, sizeof(char), 1, hndfile);
			diff = (int)bchar;
			break;
		case 1:
			fread(&bshort, sizeof(short), 1, hndfile);
			diff = (int)bshort;
			break;
		case 2:
			fread(&blong, sizeof(long), 1, hndfile);
			diff = (int)blong;
			break;
		}

		hnd[i] = r21 + diff - r11 + r12;
		//if (((float)r21 + (float)diff - (float)r11 + (float)r12) > (float)0xFFFFFFFF)
		//	printf("uint32 overflow\n"), fflush(stdout);

	}

	// Compute half-sized raw images
	for (uint y = 0; y < RAW_ROWS; ++y)
	{
		for (uint x = 0; x < RAW_COLS; ++x)
		{
			// Downsize and divide value by 2.5 to account for observed 
			// spillover beyond val=65535
			float newval = 0.4f*0.25f*static_cast<float>
			(
				static_cast<float>(hnd[2*y*HND_COLS + 2*x]) + 
				static_cast<float>(hnd[2*y*HND_COLS + 2*x + 1]) + 
				static_cast<float>(hnd[(2*y + 1)*HND_COLS + 2*x]) + 
				static_cast<float>(hnd[(2*y + 1)*HND_COLS + 2*x + 1])
			);

			if (newval > (float)0xFFFF)
				printf("uint16 overflow\n"), fflush(stdout);

			raw[y*RAW_COLS + x]= (uint16)newval;
		}
	}

	fwrite(raw, RAW_FILE_SIZE, 1, rawfile);
}